using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Jobs;
using UnityEngine.UIElements;
using static UnityEditor.PlayerSettings;


public struct AgentData : IComponentData
{
    public Vector3 position;    // 位置（float3比Vector3更高效）
    public Vector3 speed;    // 速度
    public Vector3 force; // 加速度
    public key3 key;
    public int bh;
    
}
public struct key3 : IEquatable<key3>
{
    public int x;
    public int y;
    public int z;

    public key3(int xx, int yy, int zz)
    {
        x = xx; y = yy; z = zz;
    }
    // 实现 IEquatable<key3> 接口的 Equals 方法
    public bool Equals(key3 other)
    {
        return x == other.x && y == other.y && z == other.z;
    }

    // 重写 object 的 Equals 方法
    public override bool Equals(object obj)
    {
        return obj is key3 other && Equals(other);
    }

    // 重写 GetHashCode 方法，对性能至关重要
    // 一个好的哈希码应尽可能减少冲突
    public override int GetHashCode()
    {
        unchecked // 允许整数溢出而不报错
        {
            int hash = 17;
            hash = hash * 13 + x.GetHashCode();
            hash = hash * 13 + y.GetHashCode();
            hash = hash * 13 + z.GetHashCode();
            return hash;
        }
    }
}


[BurstCompile]
struct CalculatePositionsJob : IJobParallelFor
{
    public NativeArray<AgentData> AgentDataArray;
   
    [ReadOnly] public float maxSpeed;
    [ReadOnly] public float maxSteeringForce;
    [ReadOnly] public float responseRadius;

    [ReadOnly] public float attractWeight;
    [ReadOnly] public float rejectWeight;
    [ReadOnly] public float alignWeight;
    [ReadOnly] public float targetWeight;

    [ReadOnly] public NativeParallelMultiHashMap<key3, AgentData> agentDataDictionary;


    [ReadOnly] public Vector3 targetpos;
     public float deltaTime;
    public void Execute(int index)
    {
        if(deltaTime <= 0) 
        { return; }
        Vector3 attrac = Vector3.zero;
        Vector3 reject = Vector3.zero;
        Vector3 align = Vector3.zero;
        Vector3 target = Vector3.zero;

        key3 key = AgentDataArray[index].key;
        AgentData agentData = AgentDataArray[index];
        int agentcnt = 0;

        for (int i = key.x - 1; i <= key.x + 1; i++)
        {
            for (int l = key.y - 1; l <= key.y + 1; l++)
            {
                for (int j = key.z - 1; j <= key.z + 1; j++)
                {
                    key3 ckey=new(i, l, j);
                    AgentData Agent;
                    NativeParallelMultiHashMapIterator<key3> iterator;
                    if( agentDataDictionary.TryGetFirstValue(ckey,out Agent,out iterator))
                    {
                        do
                        {
                            if(Agent.bh==agentData.bh)
                            { continue; }
                            float distance = Vector3.Distance(agentData.position, Agent.position);
                            if (distance <= responseRadius)
                            {
                                attrac += Agent.position;

                                reject += (agentData.position - Agent.position) / distance;

                                align += Agent.speed;

                                agentcnt++;
                            }

                        }
                        while (agentDataDictionary.TryGetNextValue(out Agent, ref iterator));
                    }
                }
            }
        }

        target = (targetpos - agentData.position).normalized * targetWeight;

        if (agentcnt > 0)
        {
            attrac = (attrac / agentcnt - agentData.position) * attractWeight;

            reject = reject * rejectWeight;

            align = align.normalized * alignWeight;
        }
        //Debug.Log(agentData.speed+" "+ agentData.force);
        agentData.force = attrac + reject + align + target;
       
        agentData.force = Vector3.ClampMagnitude(agentData.force, maxSteeringForce);
       
        agentData.speed += agentData.force * deltaTime;
        
        agentData.speed = Vector3.ClampMagnitude(agentData.speed, maxSpeed);
       
        agentData.position += agentData.speed * deltaTime;

        AgentDataArray[index]= agentData;
        

    }
}
[BurstCompile]
struct ApplyTransformsJob : IJobParallelForTransform
{
    public NativeArray<AgentData> AgentDataArray;
    public void Execute(int index, TransformAccess transform)
    {
        transform.position = AgentDataArray[index].position;
        if(AgentDataArray[index].speed!=Vector3.zero)
        {
            Quaternion targetRotation = Quaternion.LookRotation(AgentDataArray[index].speed, Vector3.up);
            transform.rotation = targetRotation;
        }
    }
}

public class NewAgentManager : MonoBehaviour
{
    
    public NativeArray<AgentData> AgentDataArray;
    public TransformAccessArray transformAccessArray;

    public NativeParallelMultiHashMap<key3, AgentData> agentDataDictionary;

    public int GridRadius;

    public int maxcnt;
    public Config config;
    public GameObject feb;
    public GameObject target;

    void Start()
    {
        AgentDataArray = new NativeArray<AgentData>(maxcnt, Allocator.Persistent);
        transformAccessArray = new TransformAccessArray(maxcnt);
        agentDataDictionary= new NativeParallelMultiHashMap<key3, AgentData>(maxcnt, Allocator.Persistent);


        for (int i = 0; i < maxcnt; i++)
        {
            float randomX = UnityEngine.Random.Range(-20f, 20f);
            float randomY = UnityEngine.Random.Range(-20f, 20f);
            float randomZ = UnityEngine.Random.Range(-20f, 20f);
            Vector3 randomVector = new Vector3(randomX, randomY, randomZ);

            transformAccessArray.Add(Instantiate(feb, randomVector, Quaternion.identity).transform);

            AgentData agentData = new AgentData();
            agentData.position = randomVector;
            agentData.speed = Vector3.zero;
            agentData.force = Vector3.zero;
            agentData.key = UpdateGridKey(randomVector);
            agentData.bh = i;
            AgentDataArray[i] = agentData;
            addAgent(agentData.key, AgentDataArray[i]);
            //Debug.Log(AgentDataArray[i].position);
        }
    }
    // Update is called once per frame
    void Update()
    {

        var calculateJob = new CalculatePositionsJob
        {
            AgentDataArray = AgentDataArray,

            maxSpeed = config.maxSpeed,
            maxSteeringForce = config.maxSteeringForce,
            responseRadius = config.responseRadius,

            attractWeight = config.attractWeight,
            rejectWeight = config.rejectWeight,
            alignWeight = config.alignWeight,
            targetWeight = config.targetWeight,
            //maxSpeed = 20,
            //maxSteeringForce = 20,
            //responseRadius = 10,

            //attractWeight = 20,
            //rejectWeight = 20,
            //alignWeight = 20,
            //targetWeight = 100,

            agentDataDictionary = agentDataDictionary,
            targetpos = target.transform.position,
            deltaTime = Time.deltaTime
        };

        var applyJob = new ApplyTransformsJob
        {

            AgentDataArray = AgentDataArray
        };
        
        JobHandle calculateHandle = calculateJob.Schedule(maxcnt, 64);
        JobHandle applyHandle = applyJob.Schedule(transformAccessArray, calculateHandle);
        applyHandle.Complete();

        agentDataDictionary.Clear();
        foreach (var agentData in AgentDataArray)
        {
            addAgent(agentData.key, agentData);
        }
        
    }

    void OnDestroy()
    {
        // 至关重要：释放所有Native资源，避免内存泄漏
        if (transformAccessArray.isCreated)
            transformAccessArray.Dispose();
        if (AgentDataArray.IsCreated)
            AgentDataArray.Dispose();
        if (agentDataDictionary.IsCreated)
            agentDataDictionary.Dispose();  
    }
    public key3 UpdateGridKey(Vector3 Pos)
    {
        int x, y, z;
        x = (int)(Pos.x / GridRadius);
        y = (int)(Pos.y / GridRadius);
        z = (int)(Pos.z / GridRadius);

        key3 key = new key3(x, y, z);
        return key;
    }
    public void addAgent(key3 key, AgentData theagent)
    {
        agentDataDictionary.Add(key, theagent);
    }

    //public void removeAgent(key3 key, AgentData theagent)
    //{
    //    if (!agentDataDictionary.ContainsKey(key))
    //    {
    //        agentDataDictionary.Add(key, new List<AgentData>());
    //    }
    //    agentDataDictionary[key].Remove(theagent);
    //}
}

